library(tidyverse)
library(plotly)
load("wireless.rda")
wireless_feature = wireless[,3:7] %>%
  mutate(d_S1 = apply(wireless[,1:2], 1, function(x){sqrt(sum((x - as.numeric(AP[1,]))^2))})) %>%
  mutate(d_S2 = apply(wireless[,1:2], 1, function(x){sqrt(sum((x - as.numeric(AP[2,]))^2))})) %>%
  mutate(d_S3 = apply(wireless[,1:2], 1, function(x){sqrt(sum((x - as.numeric(AP[3,]))^2))})) %>%
  mutate(d_S4 = apply(wireless[,1:2], 1, function(x){sqrt(sum((x - as.numeric(AP[4,]))^2))})) %>%
  mutate(d_S5 = apply(wireless[,1:2], 1, function(x){sqrt(sum((x - as.numeric(AP[5,]))^2))}))
plot(x=wireless$x, y=wireless$y)
points(x=AP$x,y=AP$y, col="red", cex=1)
# exploring relationship between distance and signal strength
# without the loss of generality, use X
ap1 = as.numeric(AP[1,])
distances = apply(wireless[,1:2], 1, function(x){sqrt(sum((x - ap1)^2))})
par(mfrow=c(2,2))
plot(y=(distances[distances < cutoff])^2, x=-wireless$S1[distances < cutoff])
plot(x=log(-wireless$S1[distances < cutoff]), y=2*log(distances[distances < cutoff]))
plot(y=distances[distances < cutoff], x=-wireless$S1[distances < cutoff])
plot(x=log(-wireless$S1[distances < cutoff]), y=log(distances[distances < cutoff]))

mod = lm(wireless$S2~log(distances2))
summary(mod)

TODO: try to do that for S2, S3… as well

# exploring relationship between distance and signal strength
# without the loss of generality, use X
ap2 = as.numeric(AP[2,])
distances2 = apply(wireless[,1:2], 1, function(x){sqrt(sum((x - ap2)^2))})
par(mfrow=c(1,2))
plot(y=distances2, x=wireless$S2)
plot(x=distances2, y=-wireless$S2)

cutoff = 100
sample_size = 40
num_iter = 1000
k_s = numeric(num_iter)
r_s = numeric(num_iter)
for (i in 1:num_iter) {
  temp_indices = sample((1:254)[distances < cutoff], sample_size)
  temp_mod = lm(wireless_feature[temp_indices ,1]~distances[temp_indices])
  k_s[i] = temp_mod$coefficients[2]
  r_s[i] = summary(temp_mod)$adj.r.squared
}
mean(k_s)
[1] -0.421104
mean(r_s)
[1] 0.8751914
# using bagging to find the coefficients, using 40 points 
k_s = numeric(num_iter)
for (i in 1:num_iter) {
  temp_indices = sample((1:254)[distances2 < cutoff], sample_size)
  temp_mod = lm(wireless_feature[temp_indices ,2]~distances2[temp_indices])
  k_s[i] = temp_mod$coefficients[2]
}
mean(k_s)
[1] -0.4250205
plot(x=(distances2)[distances2 < cutoff], y=-wireless$S2[distances2 < cutoff])
#for distance above 100, the linear relationship between signal and distance breaks
#lots of points have -92 the worse signal ever 
k = -0.42
d = apply(AP, 1, function(x){sqrt(sum((x - wireless[107,1:2])^2))})
dxy = t(apply(AP, 1, function(x) {as.numeric(wireless[107,1:2]) - x}))
ds.dxy = apply(dxy, 2, function(x) as.numeric(k * x / d))
ds.dxy
              x           y
[1,] -0.1784933 -0.38018434
[2,]  0.3837247 -0.17074941
[3,]  0.4188134  0.03154903
[4,] -0.4035757  0.11630423
[5,]  0.3511401 -0.23043571
ds = wireless_feature[86,] - wireless_feature[107,] 
k = mod1$coefficients[2]
b = mod1$coefficients[1]
signals = wireless_feature[224,]
test = t(apply(AP, 1, function(x) {as.numeric(wireless[224,1:2]) - x}))
test2 = apply(test, 2, function(x) as.numeric(k^2/(signals - b)) * x)
df_mod = lm(as.numeric(wireless_feature[223,] - wireless_feature[224,])~0 + 
                         test2[,1] + test2[,2])
summary(df_mod)

Call:
lm(formula = as.numeric(wireless_feature[223, ] - wireless_feature[224, 
    ]) ~ 0 + test2[, 1] + test2[, 2])

Residuals:
      1       2       3       4       5 
 0.2441  5.0087  1.8471 -0.5603  2.8118 

Coefficients:
           Estimate Std. Error t value Pr(>|t|)
test2[, 1]   -1.643      2.177  -0.755    0.505
test2[, 2]   -2.593      5.225  -0.496    0.654

Residual standard error: 3.501 on 3 degrees of freedom
Multiple R-squared:  0.1827,    Adjusted R-squared:  -0.3622 
F-statistic: 0.3353 on 2 and 3 DF,  p-value: 0.7389

##exploring using differentials 
##

sample_index = sample(1:nrow(wireless), 1)
sample_point = wireless[sample_index,]
sample_diff = data.frame(t(apply(wireless[-sample_index,], 1, function(x) x - as.numeric(wireless[sample_index,]))))
sample_diff_y = sample_diff[sample_diff$y == 0,]

mod = lm(x~.-y, data=sample_diff_y)
summary(mod)
basic_x = lm(x~.-y, data=wireless)
basic_y = lm(y~.-x, data=wireless)
#summary(basic_x)
#summary(basic_y)
avg_error = mean(sqrt((wireless$x - basic_x$fitted.values)^2 + (wireless$y - basic_y$fitted.values)^2))
avg_error
[1] 17.20438
plot(wireless$x, wireless$y)
points(basic_x$fitted.values, basic_y$fitted.values, col="red")
segments(wireless$x, wireless$y, basic_x$fitted.values, basic_y$fitted.values, col="blue")
n = nrow(wireless)
#train_percent = 0.6
#sample_indices = sample(1:nrow(wireless), train_percent*n)
knn_predictions = numeric(n)
pwdistances = as.matrix(dist(wireless[,3:7]))
for (i in 1:n) {
  knn_predictions[i] = (1:n)[-i][which.min(as.matrix(pdist::pdist(wireless[,(3:7)][i,], wireless[,(3:7)][-i,])))]
}
knn_x = wireless$x[knn_predictions]
knn_y = wireless$y[knn_predictions]
par(mfrow=c(1,2))
plot(density(sqrt((wireless$x - knn_x)^2 + (wireless$y - knn_y)^2)),
     main = "knn performance")

#plot(density(sqrt((wireless$x - basic_x$fitted.values)^2 + (wireless$y - basic_y$fitted.values)^2)),
#     main = "regression performance")
knn_errors = sqrt((wireless$x - knn_x)^2 + (wireless$y - knn_y)^2)
knn_avg_error = mean(knn_errors[knn_errors < 100])
knn_avg_error
[1] 10.97828
plot(wireless$x, wireless$y)
points(knn_x, knn_y, col="red")
arrows(wireless$x, wireless$y, knn_x, knn_y, col="blue", length = 0.1)
zero-length arrow is of indeterminate angle and so skippedzero-length arrow is of indeterminate angle and so skipped

knn_bad_loc = wireless[knn_errors > 20,] %>%
  mutate(error = knn_errors[knn_errors > 20]) %>%
  mutate(index = (1:254)[knn_errors > 20]) %>%
  dplyr::arrange(desc(error))
error_bar = 15
plot(wireless$x, wireless$y)
points(knn_x[knn_errors > error_bar], knn_y[knn_errors > error_bar], col="red")
arrows(wireless$x[knn_errors > error_bar], wireless$y[knn_errors > error_bar], knn_x[knn_errors > error_bar], knn_y[knn_errors > error_bar], col="blue", length = 0.1)

par(mfrow=c(1,2))
hist(knn_errors)
plot(density(knn_errors))
par(mfrow=c(1,2))
hist(wireless$x[knn_errors > error_bar])
plot(density(wireless$x[knn_errors > error_bar]))

#trying nearest neightbor + trigulation
point_index = 113
point = wireless[point_index,3:7]
point_d = pdist::pdist(point, wireless[-point_index,3:7])@dist
top_three = (1:254)[-point_index][order(point_d)[1:3]]
top_three
[1]  33 125   4
hover_text = apply(wireless_feature[,1:5],1, function(x) paste(x,collapse = "|"))
p = plot_ly(wireless, x=~x, y=~y, name = "receivers", type="scatter", 
            mode="markers", text=paste(1:254, "<br>", hover_text)) %>% 
  add_trace(x=AP$x, y=AP$y, name = "wifi post", mode="markers", text=rownames(AP)) %>%
  add_trace(x=wireless$x[top_three], y=wireless$y[top_three], 
            name = "neighbors", mode="markers", 
            text=paste(top_three, "<br>", hover_text[top_three])) %>%
  add_trace(x=wireless$x[point_index], y=wireless$y[point_index], 
            name = "neighbors", mode="markers", 
            text=paste(point_index, "<br>", hover_text[point_index]))
p
point_index = 113
point = wireless[point_index,3:7]
point_d = pdist::pdist(point, wireless[-point_index,(3:7)])@dist
top_three = (1:254)[-point_index][order(point_d)[1:3]]
neighbors = numeric(5)
for (i in 1:5) {
  neighbors[i] = (1:n)[-point_index][which.min(as.matrix(pdist::pdist(wireless[,(3:7)[-i]][point_index,], wireless[,(3:7)[-i]][-point_index,])))]
}
neighbors
[1]  33 153   9  33   4
dist(wireless[neighbors,1:2])
           33      153        9     33.1
153  38.47077                           
9    54.00000 79.62412                  
33.1  0.00000 38.47077 54.00000         
4    30.30000 59.06683 23.70000 30.30000

5

point
hover_text = apply(wireless_feature[,1:5],1, function(x) paste(x,collapse = "|"))
p = plot_ly(wireless, x=~x, y=~y, name = "receivers", type="scatter", 
            mode="markers", text=paste(1:254, "<br>", hover_text)) %>% 
  add_trace(x=AP$x, y=AP$y, name = "wifi post", mode="markers", text=rownames(AP)) %>%
  add_trace(x=wireless$x[neighbors], y=wireless$y[neighbors], 
            name = "neighbors", mode="markers", 
            text=paste(neighbors, "<br>", hover_text[neighbors])) #%>%
  #add_trace(x=wireless$x[point_index], y=wireless$y[point_index], 
  #          name = "point", mode="markers", 
  #          text=paste(point_index, "<br>", hover_text[point_index]))
  
p
knn_predictions[3]
[1] 37
neighbors = numeric(5)
for (i in 1:5) {
  neighbors[i] = (1:n)[-point_index][which.min(as.matrix(pdist::pdist(1/wireless[,(3:7)[-i]][point_index,], 1/wireless[,(3:7)[-i]][-point_index,])))]
}
neighbors

after analyzing the error, I find that point 243’s signal for AP3 is completely bad comparing to its neighbors, lets check other access points.

Some points, they only f* up on signal from an access point.

set.seed(12345)
sample_indices = sample(1:254, 50)
plot(wireless$x[sample_indices], wireless$y[sample_indices], ylim=c(0, 145), xlim=c(10, 235))
points(knn_x[sample_indices], knn_y[sample_indices], col="yellow")
points((knn_x-df_x)[sample_indices], (knn_y-df_y)[sample_indices], col="red")

segments(wireless$x[sample_indices], wireless$y[sample_indices], knn_x[sample_indices], knn_y[sample_indices], col="blue")
segments(knn_x[sample_indices], knn_y[sample_indices], (knn_x-df_x)[sample_indices], (knn_y-df_y)[sample_indices], col="green")
#segments(wireless$x, wireless$y, knn_x+df_x, knn_y+ df_y, col="green")
par(mfrow=c(1,2))
plot(wireless_feature$S1, wireless_feature$d1, main="ap1 signal to distance")
plot(wireless_feature$S2, wireless_feature$d2, main="ap2 signal to distance")

plot(wireless_feature$S3, wireless_feature$d3, main="ap3 signal to distance")
plot(wireless_feature$S4, wireless_feature$d4, main="ap4 signal to distance")

plot(wireless_feature$S5, wireless_feature$d5, main="ap5 signal to distance")
# maybe normal method for signal < 70
# modeling xlog distance when signal > 70 
plot.new()

plot(y=wireless_feature$S1, wireless_feature$d1, main="ap1 distance to signal")
plot(y=wireless_feature$S2, wireless_feature$d2, main="ap2 distance to signal")

plot(y=wireless_feature$S3, wireless_feature$d3, main="ap3 distance to signal")
plot(y=wireless_feature$S4, wireless_feature$d4, main="ap4 distance to signal")

plot(y=wireless_feature$S5, wireless_feature$d5, main="ap5 distance to signal")

From the first half of the graph, we can see for different AP, the variance spikes at different points.

Looking at the second half of the graphs.
For access point 5, the relationship between distance and signal is very weak, while
others are more stable. This may have to do with AP5 is in the center of the building.

hover_text = apply(wireless_feature,1, function(x) paste(x,collapse = "|"))
p = plot_ly(wireless, x=~x, y=~y, name = "receivers", type="scatter", 
            mode="markers", text=paste(1:254, "<br>", hover_text)) %>% 
  add_trace(x=AP$x, y=AP$y, name = "wifi post", mode="markers", text=rownames(AP))
p
kclusters = kmeans(wireless[,3:7], 5)
#kclusters$cluster

ggplot(data=wireless) +
  geom_point(aes(x=x,y=y), colour=kclusters$cluster) 
ggplot(data=wireless) +
  geom_point(aes(x=x,y=y)) +
  scale_fill_manual(kclusters$cluster)
cutoff = 68
# seems like 70 is a good cut off lets check how many points have more than 70
wireless_strong = wireless %>%
  mutate(S1 = S1 > -cutoff) %>% 
  mutate(S2 = S2 > -cutoff) %>% 
  mutate(S3 = S3 > -cutoff) %>% 
  mutate(S4 = S4 > -cutoff) %>% 
  mutate(S5 = S5 > -cutoff) 
# seems like 70 is not a good cutoff as we think
table(apply(wireless_strong[,3:7], 1, sum))

  0   1   2   3 
 19 150  80   5 
bad_locations = wireless_strong[as.numeric(apply(wireless_strong[,3:7], 1, sum)) < 2,]

plot(x=bad_locations$x, y=bad_locations$y, ylim=c(0,150), xlim=c(0,230))
points(x=AP$x,y=AP$y, col="red", cex=5)
View(data.frame(table(wireless$y)))
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKYGBge3IgbGlicmFyeX0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkocGxvdGx5KQpgYGAKCgpgYGB7ciBpbXBvcnRfZGF0YX0KbG9hZCgid2lyZWxlc3MucmRhIikKd2lyZWxlc3NfZmVhdHVyZSA9IHdpcmVsZXNzWywzOjddICU+JQogIG11dGF0ZShkMSA9IGFwcGx5KHdpcmVsZXNzWywxOjJdLCAxLCBmdW5jdGlvbih4KXtzcXJ0KHN1bSgoeCAtIGFzLm51bWVyaWMoQVBbMSxdKSleMikpfSkpICU+JQogIG11dGF0ZShkMiA9IGFwcGx5KHdpcmVsZXNzWywxOjJdLCAxLCBmdW5jdGlvbih4KXtzcXJ0KHN1bSgoeCAtIGFzLm51bWVyaWMoQVBbMixdKSleMikpfSkpICU+JQogIG11dGF0ZShkMyA9IGFwcGx5KHdpcmVsZXNzWywxOjJdLCAxLCBmdW5jdGlvbih4KXtzcXJ0KHN1bSgoeCAtIGFzLm51bWVyaWMoQVBbMyxdKSleMikpfSkpICU+JQogIG11dGF0ZShkNCA9IGFwcGx5KHdpcmVsZXNzWywxOjJdLCAxLCBmdW5jdGlvbih4KXtzcXJ0KHN1bSgoeCAtIGFzLm51bWVyaWMoQVBbNCxdKSleMikpfSkpICU+JQogIG11dGF0ZShkNSA9IGFwcGx5KHdpcmVsZXNzWywxOjJdLCAxLCBmdW5jdGlvbih4KXtzcXJ0KHN1bSgoeCAtIGFzLm51bWVyaWMoQVBbNSxdKSleMikpfSkpCmBgYAoKYGBge3Igc2VlaW5nX3BsYWNlc30KcGxvdCh4PXdpcmVsZXNzJHgsIHk9d2lyZWxlc3MkeSkKcG9pbnRzKHg9QVAkeCx5PUFQJHksIGNvbD0icmVkIiwgY2V4PTEpCmBgYAoKYGBge3J9CiMgZXhwbG9yaW5nIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGRpc3RhbmNlIGFuZCBzaWduYWwgc3RyZW5ndGgKIyB3aXRob3V0IHRoZSBsb3NzIG9mIGdlbmVyYWxpdHksIHVzZSBYCmFwMSA9IGFzLm51bWVyaWMoQVBbMSxdKQpkaXN0YW5jZXMgPSBhcHBseSh3aXJlbGVzc1ssMToyXSwgMSwgZnVuY3Rpb24oeCl7c3FydChzdW0oKHggLSBhcDEpXjIpKX0pCgpwYXIobWZyb3c9YygyLDIpKQpwbG90KHk9KGRpc3RhbmNlc1tkaXN0YW5jZXMgPCBjdXRvZmZdKV4yLCB4PS13aXJlbGVzcyRTMVtkaXN0YW5jZXMgPCBjdXRvZmZdKQoKcGxvdCh4PWxvZygtd2lyZWxlc3MkUzFbZGlzdGFuY2VzIDwgY3V0b2ZmXSksIHk9Mipsb2coZGlzdGFuY2VzW2Rpc3RhbmNlcyA8IGN1dG9mZl0pKQoKcGxvdCh5PWRpc3RhbmNlc1tkaXN0YW5jZXMgPCBjdXRvZmZdLCB4PS13aXJlbGVzcyRTMVtkaXN0YW5jZXMgPCBjdXRvZmZdKQoKcGxvdCh4PWxvZygtd2lyZWxlc3MkUzFbZGlzdGFuY2VzIDwgY3V0b2ZmXSksIHk9bG9nKGRpc3RhbmNlc1tkaXN0YW5jZXMgPCBjdXRvZmZdKSkKYGBgCgoKYGBge3J9Cm1vZCA9IGxtKHdpcmVsZXNzJFMyfmxvZyhkaXN0YW5jZXMyKSkKc3VtbWFyeShtb2QpCmBgYAoKVE9ETzoKdHJ5IHRvIGRvIHRoYXQgZm9yIFMyLCBTMy4uLiBhcyB3ZWxsCgpgYGB7cn0KIyBleHBsb3JpbmcgcmVsYXRpb25zaGlwIGJldHdlZW4gZGlzdGFuY2UgYW5kIHNpZ25hbCBzdHJlbmd0aAojIHdpdGhvdXQgdGhlIGxvc3Mgb2YgZ2VuZXJhbGl0eSwgdXNlIFgKYXAyID0gYXMubnVtZXJpYyhBUFsyLF0pCmRpc3RhbmNlczIgPSBhcHBseSh3aXJlbGVzc1ssMToyXSwgMSwgZnVuY3Rpb24oeCl7c3FydChzdW0oKHggLSBhcDIpXjIpKX0pCgpwYXIobWZyb3c9YygxLDIpKQoKcGxvdCh5PWRpc3RhbmNlczIsIHg9d2lyZWxlc3MkUzIpCnBsb3QoeD1kaXN0YW5jZXMyLCB5PS13aXJlbGVzcyRTMikKCnBsb3QoeT1kaXN0YW5jZXMsIHg9d2lyZWxlc3MkUzEpCnBsb3QoeD1kaXN0YW5jZXMsIHk9LXdpcmVsZXNzJFMxKQpgYGAKCmBgYHtyIGJhZ19pbml0fQpjdXRvZmYgPSAxMDAKc2FtcGxlX3NpemUgPSA0MApudW1faXRlciA9IDEwMDAKYGBgCgoKYGBge3IgYmFnMX0Ka19zID0gbnVtZXJpYyhudW1faXRlcikKcl9zID0gbnVtZXJpYyhudW1faXRlcikKCmZvciAoaSBpbiAxOm51bV9pdGVyKSB7CiAgdGVtcF9pbmRpY2VzID0gc2FtcGxlKCgxOjI1NClbZGlzdGFuY2VzIDwgY3V0b2ZmXSwgc2FtcGxlX3NpemUpCiAgdGVtcF9tb2QgPSBsbSh3aXJlbGVzc19mZWF0dXJlW3RlbXBfaW5kaWNlcyAsMV1+ZGlzdGFuY2VzW3RlbXBfaW5kaWNlc10pCiAga19zW2ldID0gdGVtcF9tb2QkY29lZmZpY2llbnRzWzJdCiAgcl9zW2ldID0gc3VtbWFyeSh0ZW1wX21vZCkkYWRqLnIuc3F1YXJlZAp9CgptZWFuKGtfcykKbWVhbihyX3MpCmBgYAoKYGBge3IgYmFnMn0KIyB1c2luZyBiYWdnaW5nIHRvIGZpbmQgdGhlIGNvZWZmaWNpZW50cywgdXNpbmcgNDAgcG9pbnRzIAprX3MgPSBudW1lcmljKG51bV9pdGVyKQpmb3IgKGkgaW4gMTpudW1faXRlcikgewogIHRlbXBfaW5kaWNlcyA9IHNhbXBsZSgoMToyNTQpW2Rpc3RhbmNlczIgPCBjdXRvZmZdLCBzYW1wbGVfc2l6ZSkKICB0ZW1wX21vZCA9IGxtKHdpcmVsZXNzX2ZlYXR1cmVbdGVtcF9pbmRpY2VzICwyXX5kaXN0YW5jZXMyW3RlbXBfaW5kaWNlc10pCiAga19zW2ldID0gdGVtcF9tb2QkY29lZmZpY2llbnRzWzJdCn0KCm1lYW4oa19zKQpgYGAKCgoKYGBge3J9CnBsb3QoeD0oZGlzdGFuY2VzMilbZGlzdGFuY2VzMiA8IGN1dG9mZl0sIHk9LXdpcmVsZXNzJFMyW2Rpc3RhbmNlczIgPCBjdXRvZmZdKQojZm9yIGRpc3RhbmNlIGFib3ZlIDEwMCwgdGhlIGxpbmVhciByZWxhdGlvbnNoaXAgYmV0d2VlbiBzaWduYWwgYW5kIGRpc3RhbmNlIGJyZWFrcwojbG90cyBvZiBwb2ludHMgaGF2ZSAtOTIgdGhlIHdvcnNlIHNpZ25hbCBldmVyIApgYGAKCmBgYHtyfQptb2QyID0gbG0od2lyZWxlc3MkUzJbZGlzdGFuY2VzMiA8IGN1dG9mZl1+ZGlzdGFuY2VzMltkaXN0YW5jZXMyIDwgY3V0b2ZmXSkKbW9kMSA9IGxtKHdpcmVsZXNzJFMxW2Rpc3RhbmNlcyA8IGN1dG9mZl1+ZGlzdGFuY2VzW2Rpc3RhbmNlcyA8IGN1dG9mZl0pCgp3aXJlbGVzc1tjKDEwNywgODYpLF0KYGBgCgpgYGB7cn0KayA9IC0wLjQyCgpkID0gYXBwbHkoQVAsIDEsIGZ1bmN0aW9uKHgpe3NxcnQoc3VtKCh4IC0gd2lyZWxlc3NbMTA3LDE6Ml0pXjIpKX0pCgpkeHkgPSB0KGFwcGx5KEFQLCAxLCBmdW5jdGlvbih4KSB7YXMubnVtZXJpYyh3aXJlbGVzc1sxMDcsMToyXSkgLSB4fSkpCgpkcy5keHkgPSBhcHBseShkeHksIDIsIGZ1bmN0aW9uKHgpIGFzLm51bWVyaWMoayAqIHggLyBkKSkKCmRzLmR4eQoKZHMgPSB3aXJlbGVzc19mZWF0dXJlWzg2LF0gLSB3aXJlbGVzc19mZWF0dXJlWzEwNyxdIApgYGAKCgpgYGB7cn0KayA9IG1vZDEkY29lZmZpY2llbnRzWzJdCmIgPSBtb2QxJGNvZWZmaWNpZW50c1sxXQoKc2lnbmFscyA9IHdpcmVsZXNzX2ZlYXR1cmVbMjI0LF0KCnRlc3QgPSB0KGFwcGx5KEFQLCAxLCBmdW5jdGlvbih4KSB7YXMubnVtZXJpYyh3aXJlbGVzc1syMjQsMToyXSkgLSB4fSkpCgp0ZXN0MiA9IGFwcGx5KHRlc3QsIDIsIGZ1bmN0aW9uKHgpIGFzLm51bWVyaWMoa14yLyhzaWduYWxzIC0gYikpICogeCkKCmRmX21vZCA9IGxtKGFzLm51bWVyaWMod2lyZWxlc3NfZmVhdHVyZVsyMjMsXSAtIHdpcmVsZXNzX2ZlYXR1cmVbMjI0LF0pfjAgKyAKICAgICAgICAgICAgICAgICAgICAgICAgIHRlc3QyWywxXSArIHRlc3QyWywyXSkKCnN1bW1hcnkoZGZfbW9kKQoKCmBgYAoKYGBge3J9CiMxMi8zMS8yMDE3IGRpZmZlcmVudGlhbCBhdHRlbnRzCmRpZmZfMTIgPSAod2lyZWxlc3NfZmVhdHVyZSRTMSAtIHdpcmVsZXNzX2ZlYXR1cmUkUzIpCmRpZmZfMzIgPSAod2lyZWxlc3NfZmVhdHVyZSRTNCAtIHdpcmVsZXNzX2ZlYXR1cmUkUzUpCnN1bW1hcnkobG0obG9nKHdpcmVsZXNzX2ZlYXR1cmUkZDEvd2lyZWxlc3NfZmVhdHVyZSRkMil+MCArIGRpZmZfMTIpKQoKc3VtbWFyeShsbShsb2cod2lyZWxlc3NfZmVhdHVyZSRkNC93aXJlbGVzc19mZWF0dXJlJGQ1KX4wICsgZGlmZl8zMikpCgpwbG90KCh3aXJlbGVzc19mZWF0dXJlJFMxIC0gd2lyZWxlc3NfZmVhdHVyZSRTMiksIGxvZyh3aXJlbGVzc19mZWF0dXJlJGQxL3dpcmVsZXNzX2ZlYXR1cmUkZDIpKQpgYGAKCgpgYGB7cn0KIyNleHBsb3JpbmcgdXNpbmcgZGlmZmVyZW50aWFscyAKIyMKCnNhbXBsZV9pbmRleCA9IHNhbXBsZSgxOm5yb3cod2lyZWxlc3MpLCAxKQpzYW1wbGVfcG9pbnQgPSB3aXJlbGVzc1tzYW1wbGVfaW5kZXgsXQpzYW1wbGVfZGlmZiA9IGRhdGEuZnJhbWUodChhcHBseSh3aXJlbGVzc1stc2FtcGxlX2luZGV4LF0sIDEsIGZ1bmN0aW9uKHgpIHggLSBhcy5udW1lcmljKHdpcmVsZXNzW3NhbXBsZV9pbmRleCxdKSkpKQpzYW1wbGVfZGlmZl95ID0gc2FtcGxlX2RpZmZbc2FtcGxlX2RpZmYkeSA9PSAwLF0KCm1vZCA9IGxtKHh+Li15LCBkYXRhPXNhbXBsZV9kaWZmX3kpCnN1bW1hcnkobW9kKQpgYGAKCmBgYHtyfQpiYXNpY194ID0gbG0oeH4uLXksIGRhdGE9d2lyZWxlc3MpCmJhc2ljX3kgPSBsbSh5fi4teCwgZGF0YT13aXJlbGVzcykKCiNzdW1tYXJ5KGJhc2ljX3gpCiNzdW1tYXJ5KGJhc2ljX3kpCgphdmdfZXJyb3IgPSBtZWFuKHNxcnQoKHdpcmVsZXNzJHggLSBiYXNpY194JGZpdHRlZC52YWx1ZXMpXjIgKyAod2lyZWxlc3MkeSAtIGJhc2ljX3kkZml0dGVkLnZhbHVlcyleMikpCmF2Z19lcnJvcgpgYGAKCmBgYHtyIGJhc2ljX3ByZWRpY3Rpb259CnBsb3Qod2lyZWxlc3MkeCwgd2lyZWxlc3MkeSkKcG9pbnRzKGJhc2ljX3gkZml0dGVkLnZhbHVlcywgYmFzaWNfeSRmaXR0ZWQudmFsdWVzLCBjb2w9InJlZCIpCnNlZ21lbnRzKHdpcmVsZXNzJHgsIHdpcmVsZXNzJHksIGJhc2ljX3gkZml0dGVkLnZhbHVlcywgYmFzaWNfeSRmaXR0ZWQudmFsdWVzLCBjb2w9ImJsdWUiKQpgYGAKCmBgYHtyIGJhc2ljX2tubn0KbiA9IG5yb3cod2lyZWxlc3MpCiN0cmFpbl9wZXJjZW50ID0gMC42CiNzYW1wbGVfaW5kaWNlcyA9IHNhbXBsZSgxOm5yb3cod2lyZWxlc3MpLCB0cmFpbl9wZXJjZW50Km4pCmtubl9wcmVkaWN0aW9ucyA9IG51bWVyaWMobikKCnB3ZGlzdGFuY2VzID0gYXMubWF0cml4KGRpc3Qod2lyZWxlc3NbLDM6N10pKQoKZm9yIChpIGluIDE6bikgewogIGtubl9wcmVkaWN0aW9uc1tpXSA9ICgxOm4pWy1pXVt3aGljaC5taW4oYXMubWF0cml4KHBkaXN0OjpwZGlzdCh3aXJlbGVzc1ssKDM6NyldW2ksXSwgd2lyZWxlc3NbLCgzOjcpXVstaSxdKSkpXQp9CmBgYAoKCmBgYHtyfQprbm5feCA9IHdpcmVsZXNzJHhba25uX3ByZWRpY3Rpb25zXQprbm5feSA9IHdpcmVsZXNzJHlba25uX3ByZWRpY3Rpb25zXQoKcGFyKG1mcm93PWMoMSwyKSkKcGxvdChkZW5zaXR5KHNxcnQoKHdpcmVsZXNzJHggLSBrbm5feCleMiArICh3aXJlbGVzcyR5IC0ga25uX3kpXjIpKSwKICAgICBtYWluID0gImtubiBwZXJmb3JtYW5jZSIpCiNwbG90KGRlbnNpdHkoc3FydCgod2lyZWxlc3MkeCAtIGJhc2ljX3gkZml0dGVkLnZhbHVlcyleMiArICh3aXJlbGVzcyR5IC0gYmFzaWNfeSRmaXR0ZWQudmFsdWVzKV4yKSksCiMgICAgIG1haW4gPSAicmVncmVzc2lvbiBwZXJmb3JtYW5jZSIpCgprbm5fZXJyb3JzID0gc3FydCgod2lyZWxlc3MkeCAtIGtubl94KV4yICsgKHdpcmVsZXNzJHkgLSBrbm5feSleMikKa25uX2F2Z19lcnJvciA9IG1lYW4oa25uX2Vycm9yc1trbm5fZXJyb3JzIDwgMTAwXSkKa25uX2F2Z19lcnJvcgpgYGAKCgpgYGB7cn0KcGxvdCh3aXJlbGVzcyR4LCB3aXJlbGVzcyR5KQpwb2ludHMoa25uX3gsIGtubl95LCBjb2w9InJlZCIpCmFycm93cyh3aXJlbGVzcyR4LCB3aXJlbGVzcyR5LCBrbm5feCwga25uX3ksIGNvbD0iYmx1ZSIsIGxlbmd0aCA9IDAuMSkKYGBgCgpgYGB7ciBrbm5fZXJyb3JfYW5hbHlzaXN9Cmtubl9iYWRfbG9jID0gd2lyZWxlc3Nba25uX2Vycm9ycyA+IDIwLF0gJT4lCiAgbXV0YXRlKGVycm9yID0ga25uX2Vycm9yc1trbm5fZXJyb3JzID4gMjBdKSAlPiUKICBtdXRhdGUoaW5kZXggPSAoMToyNTQpW2tubl9lcnJvcnMgPiAyMF0pICU+JQogIGRwbHlyOjphcnJhbmdlKGRlc2MoZXJyb3IpKQpgYGAKCmBgYHtyfQplcnJvcl9iYXIgPSAxNQpwbG90KHdpcmVsZXNzJHgsIHdpcmVsZXNzJHkpCnBvaW50cyhrbm5feFtrbm5fZXJyb3JzID4gZXJyb3JfYmFyXSwga25uX3lba25uX2Vycm9ycyA+IGVycm9yX2Jhcl0sIGNvbD0icmVkIikKYXJyb3dzKHdpcmVsZXNzJHhba25uX2Vycm9ycyA+IGVycm9yX2Jhcl0sIHdpcmVsZXNzJHlba25uX2Vycm9ycyA+IGVycm9yX2Jhcl0sIGtubl94W2tubl9lcnJvcnMgPiBlcnJvcl9iYXJdLCBrbm5feVtrbm5fZXJyb3JzID4gZXJyb3JfYmFyXSwgY29sPSJibHVlIiwgbGVuZ3RoID0gMC4xKQpgYGAKCmBgYHtyfQpwYXIobWZyb3c9YygxLDIpKQpoaXN0KGtubl9lcnJvcnMpCnBsb3QoZGVuc2l0eShrbm5fZXJyb3JzKSkKYGBgCgoKYGBge3J9CnBhcihtZnJvdz1jKDEsMikpCmhpc3Qod2lyZWxlc3MkeFtrbm5fZXJyb3JzID4gZXJyb3JfYmFyXSkKcGxvdChkZW5zaXR5KHdpcmVsZXNzJHhba25uX2Vycm9ycyA+IGVycm9yX2Jhcl0pKQpgYGAKCgpgYGB7cn0KI3RyeWluZyBuZWFyZXN0IG5laWdodGJvciArIHRyaWd1bGF0aW9uCnBvaW50X2luZGV4ID0gMTEzCnBvaW50ID0gd2lyZWxlc3NbcG9pbnRfaW5kZXgsMzo3XQpwb2ludF9kID0gcGRpc3Q6OnBkaXN0KHBvaW50LCB3aXJlbGVzc1stcG9pbnRfaW5kZXgsMzo3XSlAZGlzdAp0b3BfdGhyZWUgPSAoMToyNTQpWy1wb2ludF9pbmRleF1bb3JkZXIocG9pbnRfZClbMTozXV0KdG9wX3RocmVlCmBgYAoKYGBge3IgdmlzdWFsaXppbmd9CmhvdmVyX3RleHQgPSBhcHBseSh3aXJlbGVzc19mZWF0dXJlWywxOjVdLDEsIGZ1bmN0aW9uKHgpIHBhc3RlKHgsY29sbGFwc2UgPSAifCIpKQpwID0gcGxvdF9seSh3aXJlbGVzcywgeD1+eCwgeT1+eSwgbmFtZSA9ICJyZWNlaXZlcnMiLCB0eXBlPSJzY2F0dGVyIiwgCiAgICAgICAgICAgIG1vZGU9Im1hcmtlcnMiLCB0ZXh0PXBhc3RlKDE6MjU0LCAiPGJyPiIsIGhvdmVyX3RleHQpKSAlPiUgCiAgYWRkX3RyYWNlKHg9QVAkeCwgeT1BUCR5LCBuYW1lID0gIndpZmkgcG9zdCIsIG1vZGU9Im1hcmtlcnMiLCB0ZXh0PXJvd25hbWVzKEFQKSkgJT4lCiAgYWRkX3RyYWNlKHg9d2lyZWxlc3MkeFt0b3BfdGhyZWVdLCB5PXdpcmVsZXNzJHlbdG9wX3RocmVlXSwgCiAgICAgICAgICAgIG5hbWUgPSAibmVpZ2hib3JzIiwgbW9kZT0ibWFya2VycyIsIAogICAgICAgICAgICB0ZXh0PXBhc3RlKHRvcF90aHJlZSwgIjxicj4iLCBob3Zlcl90ZXh0W3RvcF90aHJlZV0pKSAlPiUKICBhZGRfdHJhY2UoeD13aXJlbGVzcyR4W3BvaW50X2luZGV4XSwgeT13aXJlbGVzcyR5W3BvaW50X2luZGV4XSwgCiAgICAgICAgICAgIG5hbWUgPSAibmVpZ2hib3JzIiwgbW9kZT0ibWFya2VycyIsIAogICAgICAgICAgICB0ZXh0PXBhc3RlKHBvaW50X2luZGV4LCAiPGJyPiIsIGhvdmVyX3RleHRbcG9pbnRfaW5kZXhdKSkKcApgYGAKCmBgYHtyfQpwb2ludF9pbmRleCA9IDExMwpwb2ludCA9IHdpcmVsZXNzW3BvaW50X2luZGV4LDM6N10KcG9pbnRfZCA9IHBkaXN0OjpwZGlzdChwb2ludCwgd2lyZWxlc3NbLXBvaW50X2luZGV4LCgzOjcpXSlAZGlzdAp0b3BfdGhyZWUgPSAoMToyNTQpWy1wb2ludF9pbmRleF1bb3JkZXIocG9pbnRfZClbMTozXV0KCgpuZWlnaGJvcnMgPSBudW1lcmljKDUpCmZvciAoaSBpbiAxOjUpIHsKICBuZWlnaGJvcnNbaV0gPSAoMTpuKVstcG9pbnRfaW5kZXhdW3doaWNoLm1pbihhcy5tYXRyaXgocGRpc3Q6OnBkaXN0KHdpcmVsZXNzWywoMzo3KVstaV1dW3BvaW50X2luZGV4LF0sIHdpcmVsZXNzWywoMzo3KVstaV1dWy1wb2ludF9pbmRleCxdKSkpXQp9CgpuZWlnaGJvcnMKZGlzdCh3aXJlbGVzc1tuZWlnaGJvcnMsMToyXSkKYGBgCjUKYGBge3J9CnBvaW50CmBgYAoKCmBgYHtyfQpob3Zlcl90ZXh0ID0gYXBwbHkod2lyZWxlc3NfZmVhdHVyZVssMTo1XSwxLCBmdW5jdGlvbih4KSBwYXN0ZSh4LGNvbGxhcHNlID0gInwiKSkKcCA9IHBsb3RfbHkod2lyZWxlc3MsIHg9fngsIHk9fnksIG5hbWUgPSAicmVjZWl2ZXJzIiwgdHlwZT0ic2NhdHRlciIsIAogICAgICAgICAgICBtb2RlPSJtYXJrZXJzIiwgdGV4dD1wYXN0ZSgxOjI1NCwgIjxicj4iLCBob3Zlcl90ZXh0KSkgJT4lIAogIGFkZF90cmFjZSh4PUFQJHgsIHk9QVAkeSwgbmFtZSA9ICJ3aWZpIHBvc3QiLCBtb2RlPSJtYXJrZXJzIiwgdGV4dD1yb3duYW1lcyhBUCkpICU+JQogIGFkZF90cmFjZSh4PXdpcmVsZXNzJHhbbmVpZ2hib3JzXSwgeT13aXJlbGVzcyR5W25laWdoYm9yc10sIAogICAgICAgICAgICBuYW1lID0gIm5laWdoYm9ycyIsIG1vZGU9Im1hcmtlcnMiLCAKICAgICAgICAgICAgdGV4dD1wYXN0ZShuZWlnaGJvcnMsICI8YnI+IiwgaG92ZXJfdGV4dFtuZWlnaGJvcnNdKSkgIyU+JQogICNhZGRfdHJhY2UoeD13aXJlbGVzcyR4W3BvaW50X2luZGV4XSwgeT13aXJlbGVzcyR5W3BvaW50X2luZGV4XSwgCiAgIyAgICAgICAgICBuYW1lID0gInBvaW50IiwgbW9kZT0ibWFya2VycyIsIAogICMgICAgICAgICAgdGV4dD1wYXN0ZShwb2ludF9pbmRleCwgIjxicj4iLCBob3Zlcl90ZXh0W3BvaW50X2luZGV4XSkpCiAgCnAKYGBgCgoKCmBgYHtyfQprbm5fcHJlZGljdGlvbnNbM10KYGBgCgpgYGB7cn0KbmVpZ2hib3JzID0gbnVtZXJpYyg1KQpmb3IgKGkgaW4gMTo1KSB7CiAgbmVpZ2hib3JzW2ldID0gKDE6bilbLXBvaW50X2luZGV4XVt3aGljaC5taW4oYXMubWF0cml4KHBkaXN0OjpwZGlzdCgxL3dpcmVsZXNzWywoMzo3KVstaV1dW3BvaW50X2luZGV4LF0sIDEvd2lyZWxlc3NbLCgzOjcpWy1pXV1bLXBvaW50X2luZGV4LF0pKSldCn0KbmVpZ2hib3JzCmBgYAoKCmFmdGVyIGFuYWx5emluZyB0aGUgZXJyb3IsIEkgZmluZCB0aGF0IHBvaW50IDI0MydzIHNpZ25hbCBmb3IgQVAzIGlzIGNvbXBsZXRlbHkgYmFkCmNvbXBhcmluZyB0byBpdHMgbmVpZ2hib3JzLCBsZXRzIGNoZWNrIG90aGVyIGFjY2VzcyBwb2ludHMuCgpTb21lIHBvaW50cywgdGhleSBvbmx5IGYqIHVwIG9uIHNpZ25hbCBmcm9tIGFuIGFjY2VzcyBwb2ludC4gCgpgYGB7cn0Kc2V0LnNlZWQoMTIzNDUpCnNhbXBsZV9pbmRpY2VzID0gc2FtcGxlKDE6MjU0LCA1MCkKcGxvdCh3aXJlbGVzcyR4W3NhbXBsZV9pbmRpY2VzXSwgd2lyZWxlc3MkeVtzYW1wbGVfaW5kaWNlc10sIHlsaW09YygwLCAxNDUpLCB4bGltPWMoMTAsIDIzNSkpCnBvaW50cyhrbm5feFtzYW1wbGVfaW5kaWNlc10sIGtubl95W3NhbXBsZV9pbmRpY2VzXSwgY29sPSJ5ZWxsb3ciKQpwb2ludHMoKGtubl94LWRmX3gpW3NhbXBsZV9pbmRpY2VzXSwgKGtubl95LWRmX3kpW3NhbXBsZV9pbmRpY2VzXSwgY29sPSJyZWQiKQoKc2VnbWVudHMod2lyZWxlc3MkeFtzYW1wbGVfaW5kaWNlc10sIHdpcmVsZXNzJHlbc2FtcGxlX2luZGljZXNdLCBrbm5feFtzYW1wbGVfaW5kaWNlc10sIGtubl95W3NhbXBsZV9pbmRpY2VzXSwgY29sPSJibHVlIikKc2VnbWVudHMoa25uX3hbc2FtcGxlX2luZGljZXNdLCBrbm5feVtzYW1wbGVfaW5kaWNlc10sIChrbm5feC1kZl94KVtzYW1wbGVfaW5kaWNlc10sIChrbm5feS1kZl95KVtzYW1wbGVfaW5kaWNlc10sIGNvbD0iZ3JlZW4iKQojc2VnbWVudHMod2lyZWxlc3MkeCwgd2lyZWxlc3MkeSwga25uX3grZGZfeCwga25uX3krIGRmX3ksIGNvbD0iZ3JlZW4iKQpgYGAKCgpgYGB7ciBzaWduYWxfYW5kX2Rpc3RhbmNlc30KcGFyKG1mcm93PWMoMSwyKSkKcGxvdCh3aXJlbGVzc19mZWF0dXJlJFMxLCB3aXJlbGVzc19mZWF0dXJlJGQxLCBtYWluPSJhcDEgc2lnbmFsIHRvIGRpc3RhbmNlIikKcGxvdCh3aXJlbGVzc19mZWF0dXJlJFMyLCB3aXJlbGVzc19mZWF0dXJlJGQyLCBtYWluPSJhcDIgc2lnbmFsIHRvIGRpc3RhbmNlIikKcGxvdCh3aXJlbGVzc19mZWF0dXJlJFMzLCB3aXJlbGVzc19mZWF0dXJlJGQzLCBtYWluPSJhcDMgc2lnbmFsIHRvIGRpc3RhbmNlIikKcGxvdCh3aXJlbGVzc19mZWF0dXJlJFM0LCB3aXJlbGVzc19mZWF0dXJlJGQ0LCBtYWluPSJhcDQgc2lnbmFsIHRvIGRpc3RhbmNlIikKcGxvdCh3aXJlbGVzc19mZWF0dXJlJFM1LCB3aXJlbGVzc19mZWF0dXJlJGQ1LCBtYWluPSJhcDUgc2lnbmFsIHRvIGRpc3RhbmNlIikKIyBtYXliZSBub3JtYWwgbWV0aG9kIGZvciBzaWduYWwgPCA3MAojIG1vZGVsaW5nIHhsb2cgZGlzdGFuY2Ugd2hlbiBzaWduYWwgPiA3MCAKcGxvdC5uZXcoKQoKcGxvdCh5PXdpcmVsZXNzX2ZlYXR1cmUkUzEsIHdpcmVsZXNzX2ZlYXR1cmUkZDEsIG1haW49ImFwMSBkaXN0YW5jZSB0byBzaWduYWwiKQpwbG90KHk9d2lyZWxlc3NfZmVhdHVyZSRTMiwgd2lyZWxlc3NfZmVhdHVyZSRkMiwgbWFpbj0iYXAyIGRpc3RhbmNlIHRvIHNpZ25hbCIpCnBsb3QoeT13aXJlbGVzc19mZWF0dXJlJFMzLCB3aXJlbGVzc19mZWF0dXJlJGQzLCBtYWluPSJhcDMgZGlzdGFuY2UgdG8gc2lnbmFsIikKcGxvdCh5PXdpcmVsZXNzX2ZlYXR1cmUkUzQsIHdpcmVsZXNzX2ZlYXR1cmUkZDQsIG1haW49ImFwNCBkaXN0YW5jZSB0byBzaWduYWwiKQpwbG90KHk9d2lyZWxlc3NfZmVhdHVyZSRTNSwgd2lyZWxlc3NfZmVhdHVyZSRkNSwgbWFpbj0iYXA1IGRpc3RhbmNlIHRvIHNpZ25hbCIpCgpgYGAKCkZyb20gdGhlIGZpcnN0IGhhbGYgb2YgdGhlIGdyYXBoLCB3ZSBjYW4gc2VlIGZvciBkaWZmZXJlbnQgQVAsCnRoZSB2YXJpYW5jZSBzcGlrZXMgYXQgZGlmZmVyZW50IHBvaW50cy4gCgpMb29raW5nIGF0IHRoZSBzZWNvbmQgaGFsZiBvZiB0aGUgZ3JhcGhzLiAgCkZvciBhY2Nlc3MgcG9pbnQgNSwgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGRpc3RhbmNlIGFuZCBzaWduYWwgaXMgdmVyeSB3ZWFrLCB3aGlsZSAgCm90aGVycyBhcmUgbW9yZSBzdGFibGUuIFRoaXMgbWF5IGhhdmUgdG8gZG8gd2l0aCBBUDUgaXMgaW4gdGhlIGNlbnRlciBvZiB0aGUgYnVpbGRpbmcuIAoKCmBgYHtyIHBsb3RseX0KaG92ZXJfdGV4dCA9IGFwcGx5KHdpcmVsZXNzX2ZlYXR1cmUsMSwgZnVuY3Rpb24oeCkgcGFzdGUoeCxjb2xsYXBzZSA9ICJ8IikpCnAgPSBwbG90X2x5KHdpcmVsZXNzLCB4PX54LCB5PX55LCBuYW1lID0gInJlY2VpdmVycyIsIHR5cGU9InNjYXR0ZXIiLCAKICAgICAgICAgICAgbW9kZT0ibWFya2VycyIsIHRleHQ9cGFzdGUoMToyNTQsICI8YnI+IiwgaG92ZXJfdGV4dCkpICU+JSAKICBhZGRfdHJhY2UoeD1BUCR4LCB5PUFQJHksIG5hbWUgPSAid2lmaSBwb3N0IiwgbW9kZT0ibWFya2VycyIsIHRleHQ9cm93bmFtZXMoQVApKQoKcApgYGAKCgpgYGB7cn0Ka2NsdXN0ZXJzID0ga21lYW5zKHdpcmVsZXNzWywzOjddLCA1KQoja2NsdXN0ZXJzJGNsdXN0ZXIKCmdncGxvdChkYXRhPXdpcmVsZXNzKSArCiAgZ2VvbV9wb2ludChhZXMoeD14LHk9eSksIGNvbG91cj1rY2x1c3RlcnMkY2x1c3RlcikgCmBgYAoKCmBgYHtyfQpnZ3Bsb3QoZGF0YT13aXJlbGVzcykgKwogIGdlb21fcG9pbnQoYWVzKHg9eCx5PXkpKSArCiAgc2NhbGVfZmlsbF9tYW51YWwoa2NsdXN0ZXJzJGNsdXN0ZXIpCgpgYGAKCgpgYGB7cn0KY3V0b2ZmID0gNjgKIyBzZWVtcyBsaWtlIDcwIGlzIGEgZ29vZCBjdXQgb2ZmIGxldHMgY2hlY2sgaG93IG1hbnkgcG9pbnRzIGhhdmUgbW9yZSB0aGFuIDcwCndpcmVsZXNzX3N0cm9uZyA9IHdpcmVsZXNzICU+JQogIG11dGF0ZShTMSA9IFMxID4gLWN1dG9mZikgJT4lIAogIG11dGF0ZShTMiA9IFMyID4gLWN1dG9mZikgJT4lIAogIG11dGF0ZShTMyA9IFMzID4gLWN1dG9mZikgJT4lIAogIG11dGF0ZShTNCA9IFM0ID4gLWN1dG9mZikgJT4lIAogIG11dGF0ZShTNSA9IFM1ID4gLWN1dG9mZikgCgojIHNlZW1zIGxpa2UgNzAgaXMgbm90IGEgZ29vZCBjdXRvZmYgYXMgd2UgdGhpbmsKdGFibGUoYXBwbHkod2lyZWxlc3Nfc3Ryb25nWywzOjddLCAxLCBzdW0pKQpgYGAKCmBgYHtyfQpiYWRfbG9jYXRpb25zID0gd2lyZWxlc3Nfc3Ryb25nW2FzLm51bWVyaWMoYXBwbHkod2lyZWxlc3Nfc3Ryb25nWywzOjddLCAxLCBzdW0pKSA8IDIsXQoKcGxvdCh4PWJhZF9sb2NhdGlvbnMkeCwgeT1iYWRfbG9jYXRpb25zJHksIHlsaW09YygwLDE1MCksIHhsaW09YygwLDIzMCkpCnBvaW50cyh4PUFQJHgseT1BUCR5LCBjb2w9InJlZCIsIGNleD01KQpgYGAKCgpgYGB7cn0KVmlldyhkYXRhLmZyYW1lKHRhYmxlKHdpcmVsZXNzJHkpKSkKCmBgYAoK